package org.ala.tiktools.tools.id;


/**
 * 雪花id生成器
 * <p>  [0 | 41位时间戳 | 10位机器码 | 12位自增量]
 *
 * @author ala
 * @date 2025-02-26 13:11
 */
public class SnowIdGenerator {
    /**
     *  时间戳长度
     */
    public final static int LENGTH_TIMESTAMP = 41;
    /**
     *  默认机器码长度
     *  <p> 支持1024机器
     */
    public final static int LENGTH_PART_ID = 10;
    /**
     *  默认子增量长度
     *  <p> 支持4096qps
     */
    public final static int LENGTH_INCREMENT = 12;
    /**
     *  各种长度自定义
     */
    protected int lengthTimestamp = LENGTH_TIMESTAMP,
                    lengthPartId = LENGTH_PART_ID,
                    lengthIncrement = LENGTH_INCREMENT;
    /**
     *  当前机器码
     */
    protected int crtPartId;
    /**
     *  当前自增量
     */
    protected int crtIncrement;
    /**
     *  上次生成id时间戳
     */
    protected volatile long lastTimestamp;
    /**
     *  是否允许篡改本地时间戳
     */
    protected boolean allowModifyLocalTimestamp;
    /**
     *  子增量满了的掩码
     */
    protected int maskFull;
    /**
     *  机器id掩码
     */
    protected int maskPartId;


    private SnowIdGenerator() {}
    
    public static SnowIdGenerator build(int partId) {
        return SnowIdGenerator.build(partId, true);
    }
    public static SnowIdGenerator build(int partId, int lengthPartId, int lengthIncrement) {
        return SnowIdGenerator.build(LENGTH_TIMESTAMP, lengthPartId, lengthIncrement, partId, true);
    }
    public static SnowIdGenerator build(int partId, boolean allowModifyLocalTimestamp) {
        return SnowIdGenerator.build(LENGTH_TIMESTAMP, LENGTH_PART_ID, LENGTH_INCREMENT, partId, allowModifyLocalTimestamp);
    }
    public static SnowIdGenerator build(int lengthTimestamp, int lengthPartId, int lengthIncrement, int partId, boolean allowModifyLocalTimestamp) {
        SnowIdGenerator g = new SnowIdGenerator();
        g.lengthIncrement = lengthIncrement;
        g.lengthTimestamp = lengthTimestamp;
        g.lengthPartId = lengthPartId;
        g.maskFull = (1<<lengthIncrement) - 1;
        g.crtIncrement = 0;
        g.crtPartId = partId & ((1<<lengthPartId) - 1);
        g.maskPartId = g.crtPartId << lengthIncrement;
        g.allowModifyLocalTimestamp = allowModifyLocalTimestamp;
        return g;
    }
    /**
     *  生成id
     *  @return [id，生成id的时间戳，生成id的机器码，生成id的自增码]
     */
    public synchronized long id() {
        long now = System.currentTimeMillis();
        if (now < lastTimestamp && !allowModifyLocalTimestamp) {
            throw new RuntimeException("本地时间戳被篡改，无法生成id");
        }

        int incr = maskFull;
        while (incr >= maskFull) {
            while (crtIncrement >= maskFull && now == System.currentTimeMillis()) {}
            now = System.currentTimeMillis();
            if (now > lastTimestamp) {
                crtIncrement = 0;
                lastTimestamp = now;
            } else {
                crtIncrement++;
            }
            incr = crtIncrement;
        }

        return (now << (lengthPartId + lengthIncrement)) | maskPartId | crtIncrement;
    }
    /**
     *  取当初生成id的时间戳
     */
    public long timestamp(long id) {
        return id >>> (lengthIncrement + lengthPartId);
    }
}
